

use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use syn::parse::{Parse, ParseStream}; // 用于解析函数参数
 use syn::punctuated::Punctuated;//解析函数参数和其他结构时使用，能够处理具有逗号等分隔符的序列。
use syn::spanned::Spanned; // 用于标记语法元素的位置
/**
parenthesized: 用于解析带括号的表达式，常见于函数参数列表等场景。
parse_macro_input: 一个便捷函数，将输入的 TokenStream 解析为指定的结构，通常用于宏的实现。
token: 提供了用于处理 Rust 语法中的分隔符和标记的相关类型。
Abi: 代表外部函数调用的 ABI（应用二进制接口），通常用于定义函数的调用约定。
Attribute: 表示 Rust 中的属性，通常用于添加元数据到函数、结构体等。
Block: 表示 Rust 的代码块，一个 {} 括起来的区域，可以包含多行 Rust 代码。
Expr: 表示任何表达式，Rust 中的所有表达式（如数字、函数调用等）均被视为 Expr。
FnArg: 表示函数参数，可以是具体参数或接收者（self）。
Generics: 表示 Rust 中的泛型参数，用于支持类型参数化。
PatType: 表示带有模式的类型，通常用于函数参数的类型定义。
ReturnType: 表示函数的返回类型，可以是某种具体类型或没有返回（即返回 ())。
Token: 表示一般的标记，Rust 语言中的关键词或符号。
Visibility: 表示可见性修饰符，例如 pub、private 等，用于定义结构、函数等的可见性范围。
*/
use syn::{parenthesized, parse_macro_input, token, Abi, Attribute, Block, DeriveInput, Expr, FnArg, Generics, PatType, ReturnType, Token, Visibility};  // 用于解析函数签名

struct Args {
    parsed: Punctuated<PatType, Token![,]>,
    required: usize,
    optional: Vec<(PatType, Expr)>,
}

impl Parse for Args {

    fn parse(input: ParseStream) -> syn::Result<Self> {
        let mut args = Punctuated::new();
        let mut has_optional = false;
        let mut required = 0;
        let mut optional = Vec::new();

        while !input.is_empty() {
            let fn_arg = input.parse::<FnArg>()?;

            let pat = match fn_arg {
                FnArg::Receiver(r) => {
                    return Err(syn::Error::new(
                        r.span(),
                        "self in default_args! is not support in this version",
                    ));
                }
                FnArg::Typed(pat) => pat,
            };

            if input.parse::<Option<Token![=]>>()?.is_some() {
                has_optional = true;
                optional.push((pat.clone(), input.parse()?));
            } else if has_optional {
                return Err(syn::Error::new(
                    pat.span(),
                    "required argument cannot come after optional argument",
                ));
            } else {
                required += 1;
            }

            args.push_value(pat);

            if input.is_empty() {
                break;
            }

            args.push_punct(input.parse()?);
        }

        Ok(Args {
            parsed: args,
            required,
            optional,
        })
    }
}

impl ToTokens for Args {

    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        self.parsed.to_tokens(tokens)
    }
}

/// Module for export keyword
///
/// export keyword would make macro export (by adding `#[macro_export]`
mod export {
    use syn::custom_keyword;

    custom_keyword!(export);   // 定义export 类型结构体
}

/// Structure for Default Argument function
///
/// This contains the signature of function like
/// `#[hello] export pub const async unsafe extern "C" fn crate::foo::bar<T>(a: T, b: u32 = 0) -> String where T: Display { format!("{}, {}", a, b) }`
struct DefaultArgs {
    attrs: Vec<Attribute>,
    export: Option<export::export>,
    vis: Visibility,
    constness: Option<Token![const]>,
    asyncness: Option<Token![async]>,
    unsafety: Option<Token![unsafe]>,
    abi: Option<Abi>,
    fn_token: Token![fn],
    crate_path: Option<(Token![crate], Token![::])>,
    fn_path: Punctuated<Ident, Token![::]>,
    fn_name: Ident,
    generics: Generics,
    paren_token: token::Paren,
    args: Args,
    ret: ReturnType,
    body: Block,
}

impl Parse for DefaultArgs {
    /// Parse function for `DefaultArgs`
    ///
    /// ## Errors
    ///
    /// - when path don't start with `crate`: `path should start with crate`
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attrs = input.call(Attribute::parse_outer)?;
        let export = input.parse()?;
        let vis = input.parse()?;
        let constness = input.parse()?;
        let asyncness = input.parse()?;
        let unsafety = input.parse()?;
        let abi = input.parse()?;
        let fn_token = input.parse()?;

        let mut fn_path: Punctuated<Ident, Token![::]> = Punctuated::new();
        let crate_token = input.parse::<Option<Token![crate]>>()?;
        let crate_path = if let Some(token) = crate_token {
            let crate_colon_token = input.parse::<Token![::]>()?;
            Some((token, crate_colon_token))
        } else {
            None
        };

        loop {
            fn_path.push_value(input.parse()?);
            if input.peek(Token![::]) {
                fn_path.push_punct(input.parse()?);
            } else {
                break;
            }
        }

        if crate_path.is_none() && fn_path.len() > 1 {
            return Err(syn::Error::new(
                fn_path.first().unwrap().span(),
                "path should start with crate",
            ));
        }
        let fn_name = fn_path.pop().unwrap().into_value();

        let mut generics: Generics = input.parse()?;
        let content;
        let paren_token = parenthesized!(content in input);
        let args = content.parse()?;
        let ret = input.parse()?;
        generics.where_clause = input.parse()?;
        let body = input.parse()?;

        Ok(DefaultArgs {
            attrs,
            export,
            vis,
            constness,
            asyncness,
            unsafety,
            abi,
            fn_token,
            crate_path,
            fn_path,
            fn_name,
            generics,
            paren_token,
            args,
            ret,
            body,
        })
    }
}

impl ToTokens for DefaultArgs {
    /// This function changes to normal signature of function
    /// It would not print `export` and change the name with under bar attached
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        for i in &self.attrs {
            i.to_tokens(tokens);
        }
        self.vis.to_tokens(tokens);
        self.constness.to_tokens(tokens);
        self.asyncness.to_tokens(tokens);
        self.unsafety.to_tokens(tokens);
        self.abi.to_tokens(tokens);
        self.fn_token.to_tokens(tokens);
        format_ident!("{}_", &self.fn_name).to_tokens(tokens);
        self.generics.lt_token.to_tokens(tokens);
        self.generics.params.to_tokens(tokens);
        self.generics.gt_token.to_tokens(tokens);
        self.paren_token.surround(tokens, |tokens| {
            self.args.to_tokens(tokens);
        });
        self.ret.to_tokens(tokens);
        self.generics.where_clause.to_tokens(tokens);
        self.body.to_tokens(tokens);
    }
}

/// Make unnamed arguments in macro
/// - `count`: how many arguments
/// - `def`: if it would be used in macro definition (will add `expr`)
fn unnamed_args(count: usize, def: bool) -> proc_macro2::TokenStream {
    (0..count)
        .map(|i| {
            let item = format_ident!("u{}", i);
            if def {
                if i == 0 {
                    quote! { $#item:expr }
                } else {
                    quote! { , $#item:expr }
                }
            } else if i == 0 {
                quote! { $#item }
            } else {
                quote! { , $#item }
            }
        })
        .collect()
}

/// Make named arguments in definition of macro
/// - `front_comma`: if it needs a front comma
/// - `input`: default args
/// - `macro_index`: mapped index of argument in function from macro
fn named_args_def(
    front_comma: bool,
    input: &DefaultArgs,
    macro_index: &[usize],
) -> proc_macro2::TokenStream {
    macro_index
        .iter()
        .enumerate()
        .map(|(j, i)| {
            let item = format_ident!("n{}", i);
            let pat = &input.args.optional[*i].0.pat;
            if !front_comma && j == 0 {
                quote! { #pat = $#item:expr }
            } else {
                quote! { , #pat = $#item:expr }
            }
        })
        .collect()
}

/// Make names arguments in macro
/// - `front_comma`: if it needs a front comma
/// - `input`: default args
/// - `offset`: offset of named argument
/// - `func_index`: whether if the function argument is provided
fn named_args(
    front_comma: bool,
    input: &DefaultArgs,
    offset: usize,
    func_index: &[bool],
) -> proc_macro2::TokenStream {
    func_index
        .iter()
        .enumerate()
        .map(|(i, provided)| {
            let inner = if *provided {
                let item = format_ident!("n{}", i + offset);
                quote! { $#item }
            } else {
                let item = &input.args.optional[i + offset].1;
                quote! { ( #item ) }
            };

            if !front_comma && i == 0 {
                quote! { #inner }
            } else {
                quote! { , #inner }
            }
        })
        .collect()
}

/// Generate one arm of macro
/// - `input`: default args
/// - `unnamed_cnt`: unnamed argument count
/// - `offset`: offset of named argument
/// - `macro_index`: mapped index of argument in function from macro
/// - `func_index`: whether if the function argument is provided
fn generate(
    input: &DefaultArgs,
    unnamed_cnt: usize,
    offset: usize,
    macro_index: &[usize],
    func_index: &[bool],
) -> proc_macro2::TokenStream {
    let fn_name = format_ident!("{}_", input.fn_name);

    let unnamed_def = unnamed_args(unnamed_cnt, true);
    let unnamed = unnamed_args(unnamed_cnt, false);

    let named_def = named_args_def(unnamed_cnt != 0, input, macro_index);
    let named = named_args(unnamed_cnt != 0, input, offset, func_index);

    if input.crate_path.is_some() {
        let fn_path = &input.fn_path;
        quote! {
            (#unnamed_def #named_def) => {
                $crate::#fn_path #fn_name(#unnamed #named)
            };
        }
    } else {
        quote! {
            (#unnamed_def #named_def) => {
                #fn_name(#unnamed #named)
            };
        }
    }
}

/// Generate macro arms recursively
/// - `input`: default args
/// - `unnamed_cnt`: unnamed argument count
/// - `offset`: offset of named argument
/// - `macro_index`: mapped index of argument in function from macro
/// - `func_index`: whether if the function argument is provided
/// - `stream`: token stream to append faster
fn generate_recursive(
    input: &DefaultArgs,
    unnamed_cnt: usize,
    offset: usize,
    macro_index: &mut Vec<usize>,
    func_index: &mut Vec<bool>,
    stream: &mut proc_macro2::TokenStream,
) {
    stream.append_all(generate(
        input,
        unnamed_cnt,
        offset,
        macro_index,
        func_index,
    ));

    for i in 0..func_index.len() {
        if func_index[i] {
            continue;
        }

        func_index[i] = true;
        macro_index.push(i + offset);
        generate_recursive(input, unnamed_cnt, offset, macro_index, func_index, stream);
        macro_index.pop();
        func_index[i] = false;
    }
}

/// Generates all macro arms
/// - `input`: default args
fn generate_macro(input: &DefaultArgs) -> proc_macro2::TokenStream {
    let mut stream = proc_macro2::TokenStream::new();

    for i in 0..=input.args.optional.len() {
        let mut macro_index = Vec::new();
        let mut func_index = vec![false; input.args.optional.len() - i];
        generate_recursive(
            input,
            input.args.required + i,
            i,
            &mut macro_index,
            &mut func_index,
            &mut stream,
        );
        println!(  "  {}  ", stream.to_string()  ) ;
    }

    stream
}


#[proc_macro]
pub fn default_args(input: TokenStream) -> TokenStream {
    let input: DefaultArgs  = parse_macro_input!(input ); // 解析函数签名
    println!("  input  {:?}, end  ",   quote! { #input } .to_string()  ) ; // 输出函数签名
    let name = &input.fn_name; // 取函数名
    let export = if input.export.is_some() {
        quote! { #[macro_export] } //输出宏导出到rust代码
    } else {
        quote! {} // 生成rust代码
    };
   print!(" export  {:?} , end  ",    quote! { #[macro_export] } .to_string() ) ; // 输出宏导出到rust代码
    let inner = generate_macro(&input);
    // 返回经过处理的函数宏
    let output = quote! {
          #input // 原函数签名  demo_
          #export
         macro_rules! #name {
            #inner  // 导出的过程宏
        }
    };
      println!(  "  {}", output );
     return output.to_string().parse().unwrap();
}

